using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using Object = UnityEngine.Object;

namespace Function.SAsset
{
    public static class AssetManager
    {
        private static Dictionary<string, GameObject> _prefabCache = new Dictionary<string, GameObject>();

        // 预设资源，外界只允许读取，不允许修改
        // 一般只有池化的对象才需要withCache，一次性对象不应该cache
        public static GameObject GetPrefab(string path, bool withCache)
        {
            if (withCache)
            {
                if (!_prefabCache.TryGetValue(path, out var prefab))
                {
                    prefab = LoadAsset<GameObject>(path);
                    if (prefab == null)
                    {
                        ($"LoadSync {path} failed").Error();
                    }

                    _prefabCache.Add(path, prefab);
                    return prefab;
                }
            }

            return LoadAsset<GameObject>(path);
        }


        // async  使用
        public static async Task<GameObject> InstantiateAwait(string path)
        {
            //XLog.Debug($" AssetManager2 InstantiateAwait GameObject Instantiate {path}");
            return await Addressables.InstantiateAsync(path);
        }

        public static async Task<GameObject> InstantiateAwait(AssetReferenceGameObject afObj)
        {
            return await afObj.InstantiateAsync();
        }

        //callback
        public static async void InstantiateAsync(string path, Action<GameObject> callback = null)
        {
            //XLog.Debug($" AssetManager2 InstantiateAsync GameObject Instantiate {path}");
            var go = await Addressables.InstantiateAsync(path);
            callback?.Invoke(go);
        }

        public static async void InstantiateAsync(AssetReferenceGameObject afObj, Action<GameObject> callback = null)
        {
            var go = await InstantiateAwait(afObj);
            callback?.Invoke(go);
        }

        //同步实例化
        public static GameObject Instantiate(string path)
        {
            // XLog.Debug($" AssetManager2 Instantiate GameObject Instantiate {path}");
            var gohandle = Addressables.InstantiateAsync(path);
            gohandle.WaitForCompletion();
            if (gohandle.Status != AsyncOperationStatus.Succeeded)
            {
                UnityEngine.Debug.LogError($"Instantiate {path} failed");
            }

            return gohandle.Result;
        }

        //释放
        public static void ReleaseInstance(GameObject obj)
        {
            if (obj != null)
            {
                Addressables.ReleaseInstance(obj);
            }
            else
            {
                ("[AssetManager]:ReleaseInstance: obj is null").Error();
            }
        }

        //async
        public static async Task<T> LoadAssetAsync<T>(string path) where T : Object
        {
            return await Addressables.LoadAssetAsync<T>(path);
        }

        //callback
        public static async void LoadAssetAsync<T>(string path, Action<T> callback = null) where T : Object
        {
            var obj = await LoadAssetAsync<T>(path);
            callback?.Invoke(obj);
        }

        public static async void LoadAssetAsync<T, T1>(T afObj, Action<T1> callback = null)
            where T : AssetReference where T1 : Object
        {
            var obj = await afObj.LoadAssetAsync<T1>();
            callback?.Invoke(obj);
        }

        // WARNING: 如果使用该函数加载GameObject，不能使用Addressables.Release释放，否则会报错！
        // 理论上，应该使用GetPrefab接口去加载预设资源
        // TODO: 后续大版本的时候完善
        //同步
        public static T LoadAsset<T>(string path)
        {
            //XLog.Debug($" AssetManager2 LoadAsset {path}");
            var op = Addressables.LoadAssetAsync<T>(path);

            op.WaitForCompletion();
            if (op.Status != AsyncOperationStatus.Succeeded)
            {
                UnityEngine.Debug.LogError($"LoadAsset {path} failed");
            }

            return op.Result;
        }

        //释放
        public static void ReleaseAsset<T>(T obj) where T : Object
        {
            if (obj != null)
            {
                Addressables.Release(obj);
            }
            else
            {
                ("[AssetManager]:ReleaseInstance: asset is null").Error();
            }
        }

        //检查key是否存在
        private static async Task<bool> CheckIfKeyExists(string keyToCheck)
        {
            AsyncOperationHandle<IList<IResourceLocation>> keysOperation =
                Addressables.LoadResourceLocationsAsync(keyToCheck, typeof(object));
            await keysOperation.Task;

            if (keysOperation.Status == AsyncOperationStatus.Succeeded && keysOperation.Result != null &&
                keysOperation.Result.Count > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}